// 泛型

function generics_eg01() {
  // function identity(arg: any): any {
  //   return arg;
  // }

  // 我们需要一种方法使返回值的类型与传入参数的类型是相同的。

  function identity<T>(arg: T): T {
    return arg;
  }

  // 这个是比较明确的传入类型参数
  // let output = identity<string>("myString");

  // 普遍方式：编译器会根据我们传入的类型推测出T的类型，针对 复杂的类型无法推断时则需要我们明确指出
  let output = identity("myString");
}

// 泛型类型
function generics_eg02() {
  function identity<T>(arg: T): T {
    return arg;
  }

  // let myIdentity: <T>(arg: T) => T = identity;
  // 可以使用不同的泛型参数名，只要在数量上和使用方式上对应上就可以
  // let myIdentity: <Y>(arg: Y) => Y = identity;
  // 也可以使用带有调用签名的对象字面量来定义泛型函数
  // let myIdentity: { <T>(arg: T): T } = identity;
  // 也可以改写为泛型接口
  // interface GenericIdentityFn {
  //   <T>(arg: T): T
  // }

  // let myIdentity: GenericIdentityFn = identity;

  // -------------------------------------------
  interface GenericIdentityFn<T> {
    (arg: T): T
  }

  let myIdentity: GenericIdentityFn<number> = identity;
  myIdentity(1)
}

// 泛型类

function generics_eg03() {
  class genericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
  }

  let myGenericNumber = new genericNumber<number>();
  myGenericNumber.zeroValue = 0;
  myGenericNumber.add = function (x, y) { return x + y; }

  let myGenericString = new genericNumber<string>();
  myGenericString.zeroValue = '1';
  myGenericString.add = function (x, y) { return x + y; }
}

// 泛型约束

function generics_eg04() {
  // interface LengthWise {
  //   length: number;
  // }

  // function loggingIdentity<T extends LengthWise>(arg: T): T {
  //   console.log(arg.length);  // Error: T doesn't have .length
  //   return arg;
  // }

  // // loggingIdentity(3)
  // loggingIdentity({ length: 1, value: 3 })
  // ----------------------------------------
  // 在泛型约束中使用类型参数
  function getProperty(obj: any, key: any) {
    return obj[key]
  }

  let x = { a: 1, b: 2, c: 3, d: 4 };

  let res1 = getProperty(x, 'a')
  console.log(res1, 'res1')
  let res2 = getProperty(x, 'm')
  console.log(res2, 'res2')
}

generics_eg04();